#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include "Thread.hpp"
#include "Cond.hpp"

//普通线程池
const static int defaultthreadnum = 3;

template <class T>
class ThreadPool
{
private:
    bool QueueIsEmpty()
    {
        return _q.empty();
    }
    void Rountine(const std::string &name)
    {
        // for test
        while (1)
        {
            //std::cout<<1<<std::endl;
            // 把任务从线程获取到线程私有：临界区->私有的栈
            T t;
            // 检测任务队列，获取任务，处理任务
            {
                LockGuard LockGuard(&_lock);
                // 判断是否有任务
                // 伪唤醒
                while (QueueIsEmpty()&&_is_running)//如果不运行了那就别休眠了
                {
                    //std::cout<<1<<std::endl;
                    _wait_thread_num++;
                    // 等条件变量
                    _cond.Wait(_lock);
                    //std::cout<<2<<std::endl;
                    _wait_thread_num--;
                }    
                if(!_is_running && QueueIsEmpty())
                {   
                    LOG(Loglevel::INFO)<<name<<"线程池要退出，并且任务队列为空，"<<name<<"退出";
                    break;
                }
                // 队列中一定有任务了,但是线程池可能退出
                //如果线程池退出了，那就是处理残留任务，如果线程池没退出，那就是正常工作
                t = _q.front();
                _q.pop();
            }
            //std::cout<<2<<std::endl;
            t(); // 处理任务不需要在临界区内部
            
            LOG(Loglevel::DEBUG) << name << "handlder task:" << t.ResulttoString();
        }
    }

public:
    ThreadPool(int threadnum = defaultthreadnum)
        : _is_running(false), _threadnum(threadnum), _wait_thread_num(0)
    {
        for (int i = 0; i < _threadnum; i++)
        {
            // Thread t(hello); 直接传肯定编译不过，因为hello里面有this指针
            // auto f=std::bind(hello,this);
            // Thread t(f);
            std::string name = "thread-" + std::to_string(i + 1);
            // Thread t([this](){
            //     this->hello();
            // },name);

            // _threads.push_back(std::move(t));

            _threads.emplace_back([this](const std::string &name)
                                  { this->Rountine(name); }, name);
        }
        LOG(Loglevel::INFO) << " thread pool obj create success";
    }
    void Start()
    {
        if (_is_running)
            return;
        _is_running = true;

        // 不start这个线程压根就每创建
        for (auto &t : _threads)
        {
            t.Start();
        }
        LOG(Loglevel::INFO) << " thread pool running success";
    }

    //线程退出的核心思想：让线程走正常的唤醒逻辑退出
    //线程池要退出时：
    //1、如果线程被唤醒&&任务队列没有任务 那就让线程退出
    //2、如果线程被唤醒&&任务队列有任务，那就让线程执行完任务再退出
    //3、线程本身没有被休眠，就让他把任务处理完，再退出。
    //如果任务队列有任务，线程是不会休眠的
    void Stop()
    {
        if(!_is_running)
            return;
        
        _is_running=false;//线程池别运行了
        
        if(_wait_thread_num)
        {
            _cond.NotifyAll();
        }
        // if (!_is_running)
        //     return;
        // _is_running = false;
        // for (auto &t : _threads)
        // {
        //     t.Stop();
        // }
    }
    void Wait()
    {
        for (auto &t : _threads)
        {
            t.Join();
        }
        LOG(Loglevel::INFO) << "thread pool wait success";
    }
    void Enqueue(const T &t)
    {
        if(!_is_running)//如果线程池要退出了就直接返回
            return;
        // 任务入队
        {
            LockGuard lockguard(&_lock); 
            _q.push(t);
            if(_wait_thread_num>0)
                _cond.NotifyOne();
        }
    }
    ~ThreadPool()
    {
    }

private:
    // 任务队列
    std::queue<T> _q; // 整体使用7的临界资源

    // 多个线程
    std::vector<Thread> _threads; // 线程池
    // 1、创建线程对象  2、让线程对象启动
    int _threadnum;
    int _wait_thread_num;

    // 保护机制
    Mutex _lock;
    Cond _cond;

    // 其他属性
    bool _is_running; // 线程池是否是启动的
};